Fonctionnement d'un ordinateur/Interruptions et pipeline

Un livre de Wikilivres.

Les interruptions et exceptions matérielles posent quelques problèmes sur les processeurs dotés d'un pipeline. Rappelons qu'une interruption ou une exception matérielle stoppent l’exécution du programme en cours et effectuent un branchement vers une routine d'interruption. Imaginons que le processeur exécute un programme et qu'une exception survienne. L'exception est le plus souvent détectée loin dans le pipeline, généralement dans les étages finaux. Avant que l'exception n'ait été détectée, le processeur a chargé des instructions dans le pipeline alors qu'elles n'auraient pas dû l'être, à savoir les instructions qui sont placées après l'instruction à l'origine de l'exception dans l'ordre du programme. Logiquement, elles n'auraient pas dû être exécutées, vu que l'exception est censée avoir fait brancher notre processeur autre part.

Exception et pipeline

Le problème est que les exceptions et interruptions sont décalées de quelques cycles par rapport à un processeur sans pipeline. On dit que les exceptions ne sont pas précises. Cela peut poser quelques problèmes à quelques programmes comme les débuggers, même si les cas sont rares. Un autre problème survient sur les processeurs avec des lectures non-bloquantes ou avec les instructions multicycles, si les techniques d’exécution dans le désordre vues dans le chapitre précédent. Les écritures dans les registres ont alors lieu dans un ordre différent de celui imposé par le programme et cela peut poser des problèmes quand on veut reprendre là où l'exception a arrêté le programme.

Pour régler ce petit problème, les concepteurs de processeur ont fait en sorte que le processeur gère lui-même exceptions et interruptions correctement. Ces techniques sont fortement reliées à l'implémentation de l’exécution dans le désordre et elles sont très importantes pour comprendre les chapitres qui vont suivre.

Les exceptions précises sur un pipeline de longueur fixe[modifier | modifier le wikicode]

Les exceptions sont détectées dans le pipeline, quand elles sont levées par un circuit. Mais elles ne sont prises en compte qu'à l'étage final du pipeline, qui est chargé d'enregistrer les données dans les registres et/ou la RAM. Pour comprendre pourquoi, imaginez que dans l'exemple du dessus, les deux instructions lèvent une exception à des étages différents. Quelle exception traiter en premier ? Il va de soi qu'on doit traiter ces exceptions dans l'ordre du programme, donc c'est celle de la première instruction qui doit être traitée. Traiter les exceptions à la fin du pipeline permet de traiter les exceptions dans leur ordre d’occurrence dans le programme.

Si une exception a lieu, il suffit de ne pas enregistrer les résultats des instructions suivantes dans les registres, jusqu’à ce que toutes les instructions fautives aient quitté le pipeline. Tout étage fournit à chaque cycle un indicateur d'exception, un groupe de quelques bits qui indiquent si une exception a eu lieu et laquelle le cas échéant. Ces bits sont propagés dans le pipeline, et passent à l'étage suivant à chaque cycle. Une fois arrivé à l'étage d’enregistrement, un circuit combinatoire vérifie ces bits (pour voir si une exception a été levée), et autorise ou interdit l'écriture dans les registres ou la mémoire en cas d'exception. Si les écritures sont interdites, elles le sont durant un certain nombre de cycles, dépendant de l'exception levée.

Propagation de l'indicateur d'exception.

De plus, il faut restaurer le program counter pour qu'il pointe vers l’instruction adéquate. Il s'agit de l'instruction qui a déclenché l'exception. Pour cela, même solution : le program counter est propagé dans le pipeline et est restauré en cas d'exception en prenant le program counter disponible à l'étage d'enregistrement.

Les exceptions précises sur un pipeline avec exécution dans le désordre[modifier | modifier le wikicode]

Il est possible d'avoir des exceptions précises avec l’exécution dans le désordre. le principe est d’exécuter les instructions dans le désordre, mais de faire les écritures dans les registres l'ordre naturel du programme. Les écritures dans les registres sont donc retardées, tant que les instructions précédentes ne sont pas terminées. Une autre solution effectue les écritures immédiatement, mais corrigent les écritures erronées en cas de problème. Les instructions démarrent dans l'ordre du programme, mais les plus rapides peuvent écrire leur résultat avant les autres.

Toutes ces techniques demandent d'ajouter un étage de pipeline pour remettre les écritures dans l'ordre du programme. Celui-ci est inséré entre l'étage d’exécution et celui d'enregistrement. Voici la liste de ces techniques :

  • un tampon de réordonnancement ;
  • un tampon d’historique ;
  • un banc de registres futurs ;
  • autre chose.

Le tampon de réordonnancement[modifier | modifier le wikicode]

Une première solution consiste à exécuter les instructions sans se préoccuper de l'ordre des écritures, avant de les remettre dans le bon ordre. Pour cela, on doit ajouter un tampon de réordonnancent, dans lesquel les résultats sont ajoutés dès qu'ils sont disponibles. Les résultats ne sont pas écrits directement dans les registres ou la RAM, mais dans le tampon de réordonnancement, afin que les résultats soient ensuite envoyés aux registres dans le bon ordre. Les résultats quittent la file d'attente dans le même ordre que celui imposé par le programme, comme s'il n'y avait pas d’exécution dans le désordre. Le tampon de réordonnancement est un mélange entre une mémoire FIFO et une mémoire associative, et nous verrons son fonctionnement dans quelques chapitres.

Tampon de réordonnancement.

Lorsqu'une instruction vient d'être décodée, elle est ajoutée dans le ROB à la suite des autres. Les instructions étant décodées dans l'ordre du programme, l'ajout des instructions dans le ROB se fait automatiquement dans l'ordre du programme. Un résultat est enregistré dans un registre lorsque les instructions précédentes (dans l'ordre du programme) ont toutes elles-mêmes enregistré leurs résultats. Dit autrement, seule l'instruction la plus ancienne peut quitter le ROB et enregistrer son résultat, les autres instructions doivent attendre. Si une exception a lieu, le ROB se débarrasse des instructions qui suivent l'instruction fautive (celle qui a déclenché l'interruption ou la mauvaise prédiction de branchement) : ces résultats ne seront pas enregistrés dans les registres architecturaux.

Le ROB est composé de plusieurs entrées, des blocs qui stockent des informations sur les résultats à écrire dans les registres ou la mémoire. Chaque entrée contient les informations suivantes :

  • L'adresse de l'instruction, obtenue en récupérant le contenu du program counter, pour savoir à quelle instruction reprendre en cas d'erreur de spéculation.
  • Le résultat de l'instruction, à écrire dans les registres.
  • Un bit de présence qui est mis à 1 quand le résultat de l'instruction est écrit dans l'entrée, qui sert à indiquer que le résultat a bien été calculé.
  • Le nom du registre de destination du résultat, histoire de savoir où l'enregistrer.
  • Un bit Exception qui précise si l'instruction a levé une exception ou non.
  • D'autres informations, suivant le processeur.

Lorsqu'un résultat quitte le ROB, pour être enregistré dans les registres, le bit Exception est vérifié pour savoir s'il faut ou non vider le ROB.

Pour rappel, certaines instructions ne renvoient pas de résultat, comme c'est le cas des branchements. La logique voudrait que ces instructions ne prennent pas d'entrée dans le ROB. Mais n'oubliez pas qu'on détermine à quelle adresse reprendre en se basant sur le program counter de l'instruction qui quitte le ROB : ne pas allouer d'entrées dans le ROB à ces instructions risque de faire reprendre le processeur quelques instruction à côté. Pour éviter cela, on ajoute quand même ces instructions dans le ROB, et on rajoute un champ qui stocke le type de l'instruction, afin que le ROB puisse savoir s'il s'agit d'une instruction qui a un résultat ou pas. On peut aussi utiliser cette indication pour savoir si le résultat doit être stocké dans un registre ou dans la mémoire.

Quand le ROB est plein, le processeur bloque les étages de chargement, décodage, etc. Cela évite de charger des instructions dans le ROB alors qu'il est plein. Notons que la fusion de plusieurs instructions machines en une seule micro-opération diminue le nombre d'instructions à stocker dans le ROB, qui stocke les micro-opérations.

Le tampon d’historique[modifier | modifier le wikicode]

Une autre solution laisse les instructions écrire dans les registres dans l'ordre qu'elles veulent, mais conserve des informations pour remettre les écritures dans l'ordre, pour retrouver les valeurs antérieures. Ces informations sont stockées dans ce qu'on appelle le tampon d’historique (history buffer ou HB).

Comme pour le ROB, le HB est une mémoire FIFO dont chaque mot mémoire est une entrée qui mémorise les informations dédiées à une instruction. Lorsqu'une instruction modifie un registre, le HB sauvegarde une copie de l'ancienne valeur, pour la restaurer en cas d'exception. Pour annuler les modifications faites par des instructions exécutées à tort, on utilise le contenu de l'HB pour remettre les registres à leur ancienne valeur. Plus précisément, on vide le HB dans l'ordre inverse d'ajout des instructions, en allant de la plus récente à la plus ancienne, jusqu'à vider totalement le HB. Une fois le tout terminé, on retrouve bien les registres tels qu'ils étaient avant l’exécution de l'exception.

Tampon d’historique.

Le banc de registres futurs[modifier | modifier le wikicode]

Avec un HB, remettre les registres à l'état normal prend du temps. Pour éviter cela, on peut utiliser deux bancs de registres. Le premier est mis à jour comme si les exceptions n’existaient pas, et conserve un état spéculatif : c'est le banc de registres futurs (future file ou FF). L'autre stocke les données valides en cas d'exception : c'est le banc de registres de retrait (retirement register file ou RRF). Le FF est systématiquement utilisé pour les lectures et écritures, sauf en cas d'exception : il laisse alors la main au RRF. Le RRF est couplé à un ROB ou un HB, histoire de conserver un état valide en cas d'exception.

Banc de registres futurs.